/*
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 *
 * Copyright @2008 the original author or authors.
 */
package com.bluestone.scripts;

import java.util.ArrayList;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


import com.bluestone.BaseAction;
import com.bluestone.action.ActionFactory;
import com.bluestone.util.Util;

/**
 * @author <a href="mailto:bluestone.daniel.q@gmail.com">daniel.q</a>
 * 
 */
public class ParserManager {

	private boolean flag = true;

	public ArrayList parseFunctionScript(String script) {
		ArrayList actions = new ArrayList();
		Node root = loadDocumentRootFromString(script);
		actions = handleTagActions(root, null);
		return actions;
	}

	public Project parseProject(String file) {
		Project project = null;
		flag = true;

		Util.info("parsing " + file + " start!");
		Node root = loadDocumentRootFromFile(file);
		if (Element.class.isInstance(root)) {
			if (Tags.PROJECT.equals(root.getNodeName())) {
				project = handleTagProject(root);
			}
		}
		if (project != null) {
			Util.info("parsing " + file + " sucessfull!");
		} else {
			Util.info("parsing " + file + " failure!");
		}
		return project;

	}

	Object parseInputObject(Input input) {
		Object obj = null;

		String file = input.getFile();
		Util.info("parsing " + file + " start!");
		Node root = loadDocumentRootFromFile(file);
		if (Element.class.isInstance(root)) {
			if (Tags.MODULE.equals(root.getNodeName())) {
				obj = handleTagModule(root);
			} else if (Tags.TESTSUITE.equals(root.getNodeName())) {
				obj = handleTagTestSuite(root);
			} else if (Tags.LIBRARY.equals(root.getNodeName())) {
				obj = handleTagLibrary(root);
			}

		}
		if (obj != null) {
			Util.info("parsing " + file + " sucessfull!");
		} else {
			Util.info("parsing " + file + " failure!");
		}

		return obj;
	}

	Library handleTagLibrary(Node node) {
		Library library = new Library();
		String name = getNodeAttribute(node, Attributes.NAME);
		ArrayList funs = handleTagFunctions(node, library);
		library.setName(name);
		library.setFunctions(funs);
		return library;
	}

	ArrayList handleTagFunctions(Node node, Object parent) {
		ArrayList list = new ArrayList();
		NodeList childNodes = node.getChildNodes();
		Node currentNode = null;
		for (int i = 0; i < childNodes.getLength(); i++) {
			currentNode = childNodes.item(i);
			if (Tags.FUNCTION.equals(currentNode.getNodeName())) {
				Function function = handleTagFunction(currentNode);
				function.setParent(parent);
				list.add(function);
			}
		}
		return list;
	}

	Function handleTagFunction(Node node) {
		Function fun = new Function();
		String name = getNodeAttribute(node, Attributes.NAME);
		for (int i = 0; i < node.getChildNodes().getLength(); i++) {
			Node temp = node.getChildNodes().item(i);
			if (Tags.SCRIPT.equals(temp.getNodeName())) {
				for (int j = 0; j < temp.getChildNodes().getLength(); j++) {
					Node temp1 = temp.getChildNodes().item(j);
					if (CDATASection.class.isInstance(temp1)) {
						String script = (((CDATASection) temp1).getData())
								.trim();
						fun.setScript(script);
						break;
					}
				}
			}
		}
		fun.setName(name);
		return fun;
	}

	Module handleTagModule(Node node) {
		Module module = new Module();
		String name = getNodeAttribute(node, Attributes.NAME);
		module.setName(name);
		ArrayList pages = handleTagPages(node, module);
		module.setPages(pages);
		return module;
	}

	ArrayList handleTagPages(Node node, Object parent) {
		ArrayList list = new ArrayList();
		NodeList childNodes = node.getChildNodes();
		Node currentNode = null;
		for (int i = 0; i < childNodes.getLength(); i++) {
			currentNode = childNodes.item(i);
			if (Tags.PAGE.equals(currentNode.getNodeName())) {
				Page page = handleTagPage(currentNode);
				page.setParent(parent);
				list.add(page);
			}
		}
		return list;
	}

	Node loadDocumentRootFromString(String content) {
		Node root = null;

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder;
		Document document;
		try {
			java.io.ByteArrayInputStream inputstream = new java.io.ByteArrayInputStream(
					content.getBytes());
			builder = factory.newDocumentBuilder();
			document = builder.parse(inputstream);
			root = document.getDocumentElement();
		} catch (Exception e) {
			e.printStackTrace();
			flag = false;
			Util.error(e);
		}

		return root;
	}

	Node loadDocumentRootFromFile(String file) {
		Node root = null;

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder;
		Document document;
		try {
			builder = factory.newDocumentBuilder();
			document = builder.parse(file);
			root = document.getDocumentElement();
		} catch (Exception e) {
			flag = false;
			Util.error(e);
		}

		return root;
	}

	Page handleTagPage(Node node) {
		Page page = new Page();
		String name = getNodeAttribute(node, Attributes.NAME);
		String browserBar = getNodeAttribute(node, Attributes.BROWSERBAR);
		String owner = getNodeAttribute(node, Attributes.OWNER);
		page.setName(name);
		if (browserBar != null) {
			page.setBrowserBar(browserBar);
		}
		page.setOwner(owner);
		ArrayList list = handleTagElements(node, page);
		page.setElements(list);
		return page;
	}

	ArrayList handleTagElements(Node node, Object parent) {
		ArrayList list = new ArrayList();
		NodeList childNodes = node.getChildNodes();
		Node currentNode;
		for (int i = 0; i < childNodes.getLength(); i++) {
			currentNode = childNodes.item(i);
			if (Tags.ELEMENT.equals(currentNode.getNodeName())) {
				com.bluestone.scripts.Element element = new com.bluestone.scripts.Element();
				String type = getNodeAttribute(currentNode, Attributes.TYPE);
				String name = getNodeAttribute(currentNode, Attributes.NAME);
				String x = getNodeAttribute(currentNode, Attributes.X);
				String y = getNodeAttribute(currentNode, Attributes.Y);
				String index = getNodeAttribute(currentNode, Attributes.INDEX);
				String forward = getNodeAttribute(currentNode,
						Attributes.FORWARD);
				String locator = getNodeAttribute(currentNode,Attributes.LOCATOR);
				element.setParent(parent);
				element.setType(type.trim());
				element.setName(name.trim());
				if (x != null && x.length() > 0)
					element.setX(Integer.parseInt(x.trim()));
				if (y != null && y.length() > 0)
					element.setY(Integer.parseInt(y.trim()));
				if (index != null && Integer.parseInt(index) >= 0) {
					element.setIndex(Integer.parseInt(index.trim()));
				}
				if (forward != null && forward.length() > 0) {
					element.setForward(forward.trim());
				}
				if(locator!=null && locator.length()>0){
					element.setLocator(locator);
				}
				list.add(element);
			}
		}
		return list;
	}

	TestCase handleTagTestCase(Node node, Object parent) {
		TestCase testcase = new TestCase();
		testcase.setParent(parent);
		String name = getNodeAttribute(node, Attributes.NAME);
		String id = getNodeAttribute(node, Attributes.ID);
		String standalone = getNodeAttribute(node, Attributes.STANDALONE);
		testcase.setName(name);
		testcase.setId(id);
		testcase.setStandalone(standalone);

		// handle command tag
		ArrayList commands = handleTagCommands(node, testcase);
		testcase.setCommands(commands);

		return testcase;
	}

	ArrayList handleTagCommands(Node node, TestCase testcase) {
		ArrayList list = new ArrayList();
		NodeList childNodes = node.getChildNodes();
		Node currentNode;
		for (int i = 0; i < childNodes.getLength(); i++) {
			currentNode = childNodes.item(i);
			if (Tags.COMMAND.equals(currentNode.getNodeName())) {
				Command command = handleTagCommand(currentNode, testcase);

				ArrayList actions = handleTagActions(currentNode, command);
				command.setActions(actions);

				list.add(command);
			}
		}
		return list;
	}

	ArrayList handleTagActions(Node node, Object parent) {
		ArrayList list = new ArrayList();
		NodeList childNodes = node.getChildNodes();
		Node currentNode;
		for (int i = 0; i < childNodes.getLength(); i++) {
			currentNode = childNodes.item(i);
			if (Tags.ACTION.equals(currentNode.getNodeName())) {
				ActionScript action = handleTagAction(currentNode, parent);
				list.add(action);
			}
		}
		return list;
	}

	ActionScript handleTagAction(Node node, Object parent) {
		ActionScript action = null;
		if (Tags.ACTION.equals(node.getNodeName())) {
			String type = getNodeAttribute(node, Attributes.TYPE);
			String target = getNodeAttribute(node, Attributes.TARGET);
			String value = getNodeAttribute(node, Attributes.VALUE);
			String delay = getNodeAttribute(node, Attributes.DELAY);
			String offset = getNodeAttribute(node, Attributes.OFFSET);
			type = type==null||"".equals(type)?"":type.toUpperCase();
			action = new ActionScript();
			
			BaseAction baseAction = ActionFactory.createAction(action);
			if(baseAction!=null){
				baseAction.setParent(parent);
				//baseAction.setExpAttributes(getExpAttributes(node));	
			}			
		}
		return action;
	}

	String getNodeAttribute(Node node, String attribute) {
		String value = null;
		Node temp = node.getAttributes().getNamedItem(attribute);
		if (temp != null) {
			value = temp.getNodeValue();
			if (value != null) {
				return value.trim();
			}
		}
		return value;
	}

	private HashMap getExpAttributes(Node node) {
		NamedNodeMap maps = node.getAttributes();
		int length = maps.getLength();
		if (length == 0) {
			return null;
		}
		HashMap attributes = new HashMap();
		for (int i = 0; i < length; i++) {
			Node temp = maps.item(i);
			String nodeName = temp.getNodeName();
			if(!isActionDefaultAttributes(nodeName)){
				attributes.put(nodeName, temp.getNodeValue());
			}
		}
		
		return attributes;
	}

	private boolean isActionDefaultAttributes(String nodeName) {
		return Attributes.TYPE.equals(nodeName)
				|| Attributes.TARGET.equals(nodeName)
				|| Attributes.VALUE.equals(nodeName)
				|| Attributes.DELAY.equals(nodeName)
				|| Attributes.OFFSET.equals(nodeName);
	}

	Command handleTagCommand(Node node, Object parent) {
		Command command = new Command();
		command.setParent(parent);
		String name = getNodeAttribute(node, Attributes.NAME);
		command.setName(name);
		return command;
	}

	TestSuite handleTagTestSuite(Node node) {
		TestSuite testSuite = new TestSuite();
		String name = getNodeAttribute(node, Attributes.NAME);
		testSuite.setName(name);

		ArrayList testcases = new ArrayList();

		NodeList childNodes = node.getChildNodes();
		Node currentNode = null;
		for (int i = 0; i < childNodes.getLength(); i++) {
			currentNode = childNodes.item(i);

			if (Tags.TESTCASE.equals(currentNode.getNodeName())) {
				TestCase testcase = handleTagTestCase(currentNode, testSuite);
				testcases.add(testcase);
			}
		}
		testSuite.setTestcases(testcases);

		return testSuite;
	}

	Project handleTagProject(Node node) {

		Browser browser = null;
		Proxy proxy = null;
		String name = getNodeAttribute(node, Attributes.NAME);
		String version = getNodeAttribute(node, Attributes.VERSION);
		String speedrate = getNodeAttribute(node, Attributes.SPEEDRATE);
		String retrycount = getNodeAttribute(node, Attributes.MAXRETRY);
		String failurecount = getNodeAttribute(node, Attributes.MAXFAILURE);
		String reportTemplate = getNodeAttribute(node,
				Attributes.REPORTTEMPLATE);
		String reportType = getNodeAttribute(node, Attributes.REPORTTYPE);
		String reportFile = getNodeAttribute(node, Attributes.REPORTFILE);
		String language = getNodeAttribute(node, Attributes.LANGUAGE);
		String country = getNodeAttribute(node, Attributes.COUNTRY);

		ArrayList executes = new ArrayList();
		ArrayList inputs = new ArrayList();
		ArrayList resources = new ArrayList();
		ArrayList properties = new ArrayList();
		Property property = new Property();
		NodeList childNodes = node.getChildNodes();
		Node currentNode = null;
		for (int i = 0; i < childNodes.getLength(); i++) {
			currentNode = childNodes.item(i);
			if (Element.class.isInstance(currentNode)) {
				if (Tags.BROWSER.equals(currentNode.getNodeName())) {
					browser = handleTagBrowser(currentNode);
					continue;
				}
				if (Tags.EXECUTE.equals(currentNode.getNodeName())) {
					Execute execute = handleTagExecute(currentNode);
					executes.add(execute);
					continue;
				}
				if (Tags.INPUT.equals(currentNode.getNodeName())) {
					Input input = handleTagInput(currentNode);
					Object obj = parseInputObject(input);
					//input.setObject(obj);
					inputs.add(input);
					continue;
				}
				if (Tags.RESOURCE.equals(currentNode.getNodeName())) {
					Resource resource = handleTagResource(currentNode);
					resources.add(resource);
					continue;
				}
				if(Tags.PROXY.equals(currentNode.getNodeName())){
					proxy = handleTagProxy(currentNode);
				}
			}
		}

		if (!flag)
			return null;

		Project project = new Project();
		project.setName(name);
		project.setVersion(version);
		if (speedrate != null) {
			project.setSpeedrate(speedrate);
		}
		if (retrycount != null) {
			project.setRetrycount(retrycount);
		}
		if (failurecount != null) {
			project.setFailurecount(failurecount);
		}
		if (reportTemplate != null) {
			project.setReportTemplate(reportTemplate);
		}
		if (reportType != null) {
			project.setReportType(reportType);
		}
		if (reportFile != null) {
			project.setReportFile(reportFile);
		}
		if (language != null) {
			project.setLanguage(language);
		}
		if (country != null) {
			project.setCountry(country);
		}
		project.setBrowser(browser);
		project.setExecutes(executes);
		project.setInputs(inputs);
		project.setResources(resources);
		project.putProperty(property);
		project.setProxy(proxy);
		return project;
	}
	
	private Proxy handleTagProxy(Node node){
		Proxy proxy = new Proxy();
		String httpport = getNodeAttribute(node, Attributes.HTTPPORT);
		String jsfile = getNodeAttribute(node,Attributes.JSFILE);
		String servername = getNodeAttribute(node,Attributes.SERVERNAME);
		
		if(httpport!=null){
			proxy.setHttpport(Integer.parseInt(httpport));
		}
		proxy.setJsfile(jsfile);
		if(servername!=null){
			proxy.setServername(servername);
		}
		return proxy;
	}
	


	Browser handleTagBrowser(Node node) {
		Browser browser = new Browser();
		String barheight = getNodeAttribute(node, Attributes.BARHEIGHT);
		String titlebarheight = getNodeAttribute(node,
				Attributes.TITLEBARHEIGHT);
		String topblankpos = getNodeAttribute(node, Attributes.TOPBLANKPOS);
		String type = getNodeAttribute(node, Attributes.TYPE);
		String path = getNodeAttribute(node, Attributes.PATH);
		String checkpos = getNodeAttribute(node, Attributes.CHECKPOS);
		if (barheight != null && barheight.length() > 0) {
			browser.setBarHeight(Integer.parseInt(barheight));
		}
		if (titlebarheight != null && titlebarheight.length() > 0) {
			browser.setTitlebarHeight(Integer.parseInt(titlebarheight));
		}
		if (topblankpos != null && topblankpos.length() > 0) {
			browser.setTopBlankPos(topblankpos);
		}
		if (type != null && type.length() > 0) {
			browser.setType(type);
		}
		if (path != null && path.length() > 0) {
			browser.setPath(path);
		}
		if (checkpos != null && checkpos.length() > 0) {
			browser.setCheckPos(checkpos);
		}
		return browser;
	}

	Execute handleTagExecute(Node node) {
		Execute execute = new Execute();
		String file = getNodeAttribute(node, Attributes.FILE);
		String outputfile = getNodeAttribute(node, Attributes.OUTPUTFILE);
		execute.setFile(file);
		execute.setOutputFile(outputfile);

		return execute;
	}

	private Resource handleTagResource(Node node) {
		Resource resource = new Resource();
		String name = getNodeAttribute(node, Attributes.RESOURCE_NAME);
		String file = getNodeAttribute(node, Attributes.RESOURCE_KEY);
		resource.setName(name);
		resource.setKey(file);
		return resource;
	}

	Input handleTagInput(Node node) {
		Input input = new Input();
		String file = getNodeAttribute(node, Attributes.FILE);
		input.setFile(file);
		return input;
	}

}
